package hr.pzrinscak.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.GroupQuery;
import org.activiti.engine.identity.User;
import org.activiti.engine.identity.UserQuery;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.cfg.IdentitySession;
import org.activiti.engine.impl.identity.GroupEntity;
import org.activiti.engine.impl.identity.IdentityInfoEntity;
import org.activiti.engine.impl.identity.UserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class IdentitySessionImpl implements IdentitySession {

    private final static Logger LOGGER = LoggerFactory.getLogger(MailService.class);
    private static Properties props = null;
    private SearchControls lSearchControls = null;

    public IdentitySessionImpl() {

        lSearchControls = new SearchControls();
        lSearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        // set time limit for query. Useful for preventing the application from
        // being blocked
        try {
            lSearchControls.setTimeLimit(new Integer(getProps().getProperty("timeout")).intValue());
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        lSearchControls.setReturningObjFlag(true);
    }

    /*====================== User ===========================*/

    @Override
    public User createNewUser(final String p_userId) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void insertUser(final User p_user) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void updateUser(final User p_updatedUser) {
        throw new UnsupportedOperationException();
    }

    public void deleteUser(final String arg0) {
        throw new UnsupportedOperationException();
    }

    public User findUserById(final String iUserId) {

        InitialDirContext lContext = null;
        User lUser = null;

        try {
            lContext = getLDAPContext();

            NamingEnumeration<SearchResult> lResults = lContext.search(getProps().getProperty("userBase"), getProps().getProperty(
                    "findUserByIdSearch"), new Object[] { iUserId }, lSearchControls);

            if (lResults.hasMore()) {
                lUser = getUser(lResults.next());
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (lContext != null) {
                    lContext.close();
                }
            } catch (NamingException e) {
            }
        }

        return lUser;
    }

    @Override
    public List<User> findUsersByGroupId(final String p_groupId) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isValidUser(final String p_userId) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public UserQuery createNewUserQuery() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<User> findUserByQueryCriteria(final Object p_query, final Page p_page) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long findUserCountByQueryCriteria(final Object p_query) {
        // TODO Auto-generated method stub
        return 0;
    }

    /*====================== Group ===========================*/

    @Override
    public Group createNewGroup(final String p_groupId) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void insertGroup(final Group p_group) {
        // TODO Auto-generated method stub

    }

    @Override
    public void updateGroup(final Group p_updatedGroup) {
        // TODO Auto-generated method stub

    }

    public void deleteGroup(final String arg0) {
        throw new UnsupportedOperationException();
    }

    public Group findGroupById(final String iGroupId) {

        InitialDirContext lContext = null;
        Group lGroup = null;

        try {
            lContext = getLDAPContext();

            NamingEnumeration<SearchResult> lResults = lContext.search(getProps().getProperty("roleBase"), getProps().getProperty(
                    "findGroupByIdSearch"), new Object[] { iGroupId }, lSearchControls);

            if (lResults.hasMore()) {
                lGroup = getGroup(lResults.next());
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (lContext != null) {
                    lContext.close();
                }
            } catch (NamingException e) {
            }
        }

        return lGroup;
    }

    public List<Group> findGroupsByUser(final String iUserId) {

        InitialDirContext lContext = null;
        List<Group> lGroups = new ArrayList<Group>();

        try {
            lContext = getLDAPContext();

            NamingEnumeration<SearchResult> lResults = lContext.search(getProps().getProperty("roleBase"), getProps().getProperty(
                    "findGroupsByUserSearch"), new Object[] { findUserById(iUserId).toString() }, lSearchControls);

            while (lResults.hasMore()) {
                lGroups.add(getGroup(lResults.next()));
            }

        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (lContext != null) {
                    lContext.close();
                }
            } catch (NamingException e) {
            }
        }

        return lGroups;
    }

    @Override
    public GroupQuery createNewGroupQuery() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<Group> findGroupByQueryCriteria(final Object p_query, final Page p_page) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long findGroupCountByQueryCriteria(final Object p_query) {
        // TODO Auto-generated method stub
        return 0;
    }

    /*====================== Membership ===========================*/

    @Override
    public void createMembership(final String p_userId, final String p_groupId) {
        // TODO Auto-generated method stub

    }

    @Override
    public void deleteMembership(final String p_userId, final String p_groupId) {
        // TODO Auto-generated method stub

    }

    /*====================== UserInfo ===========================*/

    @Override
    public IdentityInfoEntity findUserInfoByUserIdAndKey(final String p_userId, final String p_key) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public IdentityInfoEntity findUserAccountByUserIdAndKey(final String p_userId, final String p_userPassword, final String p_key) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void deleteUserInfoByUserIdAndKey(final String p_userId, final String p_key) {
        // TODO Auto-generated method stub

    }

    @Override
    public void setUserInfo(final String p_userId, final String p_userPassword, final String p_type, final String p_key, final String p_value, final String p_accountPassword,
            final Map<String, String> p_accountDetails) {
        // TODO Auto-generated method stub

    }

    @Override
    public List<String> findUserInfoKeysByUserIdAndType(final String p_userId, final String p_userInfoType) {
        // TODO Auto-generated method stub
        return null;
    }







    public List<User> findUsers() {

        List<User> lUsers = new ArrayList<User>();
        InitialDirContext lContext = null;

        try {
            lContext = getLDAPContext();

            NamingEnumeration<SearchResult> lResults = lContext.search(getProps().getProperty("userBase"), getProps().getProperty("findUsersSearch"),
                    lSearchControls);

            while (lResults.hasMore()) {
                lUsers.add(getUser(lResults.next()));
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (lContext != null) {
                    lContext.close();
                }
            } catch (NamingException e) {
            }
        }

        return lUsers;
    }

    public List<User> findUsersById(final String... iUserIds) {

        List<User> lUsers = new ArrayList<User>(iUserIds.length);

        try {
            for (String lUserId : iUserIds) {
                lUsers.add(findUserById(lUserId));
            }
        } finally {

        }

        return lUsers;
    }

    public List<User> findUsersByGroup(final String iGroup) {

        InitialDirContext lContext = null;
        List<User> lUsers = new ArrayList<User>();

        try {
            lContext = getLDAPContext();

            NamingEnumeration<SearchResult> lResults = lContext.search(getProps().getProperty("roleBase"), getProps().getProperty(
                    "findUsersByGroupSearch"), new Object[] { iGroup }, lSearchControls);

            while (lResults.hasMore()) {
                lUsers.add(getUser(lResults.next()));
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (lContext != null) {
                    lContext.close();
                }
            } catch (NamingException e) {
            }
        }

        return lUsers;
    }

    private User getUser(final SearchResult iResult) throws NamingException, IOException {

        final String iUserId = getAttributeValue(iResult, getProps().getProperty("userIdAttr"));
        final String lEmail = getAttributeValue(iResult, getProps().getProperty("userEmailAttr"));
        final String lFirstname = getAttributeValue(iResult, getProps().getProperty("userFirstNameAttr"));
        final String lLastname = getAttributeValue(iResult, getProps().getProperty("userLastNameAttr"));

        final User lUser = new UserEntity(iUserId);
        lUser.setFirstName(lFirstname);
        lUser.setLastName(lLastname);
        lUser.setEmail(lEmail);

        return lUser;
    }

    private List<User> getUsers(final SearchResult iResult) throws NamingException, IOException {
        NamingEnumeration<?> lAllAttrValues = null;

        final List<User> lUsers = new ArrayList<User>();
        final Attribute lAttr = iResult.getAttributes().get(getProps().getProperty("memberOfAttr"));

        if (lAttr != null) {
            lAllAttrValues = lAttr.getAll();
        }

        while (lAllAttrValues.hasMore()) {
            String lUserDN = (String) lAllAttrValues.next();
            lUsers.add(findUserById(getExtractedIdFromDN(lUserDN)));
        }
        return lUsers;
    }

    private Group getGroup(final SearchResult iResult) throws NamingException, IOException {

        final String iGroupId = getAttributeValue(iResult, getProps().getProperty("groupIdAttr"));
        final Group lGroup = new GroupEntity(iGroupId);

        return lGroup;
    }

    private String getExtractedIdFromDN(final String iGroupDN) {
        StringTokenizer lTok = new StringTokenizer(iGroupDN, ",");

        String lGroupCN = lTok.nextToken();

        return lGroupCN.substring(3);
    }

    private String getAttributeValue(final SearchResult iResult, final String iAttributeName) throws NamingException {
        NamingEnumeration<?> lAllAttrValues = null;
        Attribute lAttr = iResult.getAttributes().get(iAttributeName);

        if (lAttr != null) {
            lAllAttrValues = lAttr.getAll();
        }

        if (lAllAttrValues != null && lAllAttrValues.hasMore()) {
            return (String) lAllAttrValues.next();
        } else {
            return null;
        }
    }

    private InitialDirContext getLDAPContext() throws NamingException, IOException {
        InitialDirContext lLdapCtx = null;

        // Set up LDAP configuration settings
        Hashtable<String, String> lContextValues = new Hashtable<String, String>();

        lContextValues.put("java.naming.ldap.version", getProps().getProperty("ldapVersion"));
        lContextValues.put(Context.INITIAL_CONTEXT_FACTORY, getProps().getProperty("initialContextFactory"));
        lContextValues.put(Context.SECURITY_AUTHENTICATION, getProps().getProperty("authentication"));
        lContextValues.put(Context.REFERRAL, getProps().getProperty("referral"));
        lContextValues.put(Context.PROVIDER_URL, getProps().getProperty("connectionURL"));
        lContextValues.put(Context.SECURITY_PRINCIPAL, getProps().getProperty("connectionUsername"));
        lContextValues.put(Context.SECURITY_CREDENTIALS, getProps().getProperty("connectionPassword"));

        // Make LDAP connection
        lLdapCtx = new InitialDirContext(lContextValues);

        return lLdapCtx;
    }

    private static Properties getProps() throws IOException {
        if (props == null) {
            props = new Properties();
            props.load(ClassLoader.getSystemClassLoader().getResourceAsStream("ldap.properties"));
        }

        return props;
    }

}
